#! /bin/sh

#
#   Resource Agent for managing the SCST ALUA port states; this RA
#   does not manage the SCST service (modules/daemons) itself.
#

# Initialization
: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat}
. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs
SCST_SYSFS="/sys/kernel/scst_tgt"
ALUA_STATES="active nonoptimized standby unavailable offline transitioning"
TRANSITION_STATE="transitioning"
SCST_DEV_MAPS="/etc/esos_dev_maps/scst"
LUN_MAP_FLOCK="/tmp/lun_map_flock"
FLOCK_TIMEOUT=10
SG_CMD_TIMEOUT=1
ISCSIADM_TIMEOUT=5
RESCAN_SCSI_TIMEOUT=10


alua_start() {
    # Exit immediately if configuration is not valid
    alua_validate_all || exit ${?}

    # Check if SCST is loaded
    check_scst

    # If resource is already running, bail out early
    if alua_monitor; then
        ocf_log info "Resource is already running."
        return ${OCF_SUCCESS}
    fi

    # Create/update the state file
    update_state "slave"

    if ! ocf_is_true ${OCF_RESKEY_redirect_mode}; then
        # Set the local target group ALUA state initially to Slave
        ocf_log debug "alua_start() -> Setting target group" \
            "'${OCF_RESKEY_local_tgt_grp}' ALUA state to" \
            "'${OCF_RESKEY_s_alua_state}'..."
        ocf_run scstadmin -noprompt -set_tgrp_attr \
            ${OCF_RESKEY_local_tgt_grp} -dev_group \
            ${OCF_RESKEY_device_group} -attributes \
            state\=${OCF_RESKEY_s_alua_state}
        if [ ${?} -ne 0 ]; then
            error_msg="Failed to set the local target group state!"
            ocf_exit_reason "${error_msg}"
            exit ${OCF_ERR_GENERIC}
        fi
        # For now, we simply assume the other node is the Master
        ocf_log debug "alua_start() -> Setting target group" \
            "'${OCF_RESKEY_remote_tgt_grp}' ALUA state to" \
            "'${OCF_RESKEY_m_alua_state}'..."
        ocf_run scstadmin -noprompt -set_tgrp_attr \
            ${OCF_RESKEY_remote_tgt_grp} -dev_group \
            ${OCF_RESKEY_device_group} -attributes \
            state\=${OCF_RESKEY_m_alua_state}
        if [ ${?} -ne 0 ]; then
            error_msg="Failed to set the remote target group state!"
            ocf_exit_reason "${error_msg}"
            exit ${OCF_ERR_GENERIC}
        fi
    fi

    if ocf_is_true ${OCF_RESKEY_redirect_mode}; then
        # TODO: Using the dev_disk handler would be better if PR pass-through
        # was supported, however, it is not currently. Revisit in the future.
        #real_to_redirect_dev
        # We are likley using iSCSI with redirect mode, rescan here
        iscsi_sess_rescan
        scsi_bus_rescan
        # Try to set the devices to the redirected backing device to start
        bio_dev_to_redirect
        if [ ${?} -eq 0 ]; then
            # It's only safe to advertise the Slave state if all are redirected
            ocf_log debug "alua_start() -> Setting target group" \
                "'${OCF_RESKEY_local_tgt_grp}' ALUA state to" \
                "'${OCF_RESKEY_s_alua_state}'..."
            ocf_run scstadmin -noprompt -set_tgrp_attr \
                ${OCF_RESKEY_local_tgt_grp} -dev_group \
                ${OCF_RESKEY_device_group} -attributes \
                state\=${OCF_RESKEY_s_alua_state}
            if [ ${?} -ne 0 ]; then
                error_msg="Failed to set the local target group state!"
                ocf_exit_reason "${error_msg}"
                exit ${OCF_ERR_GENERIC}
            fi
        fi
    fi

    # Make sure the resource started correctly
    while ! alua_monitor; do
        ocf_log debug "alua_start() -> Resource has not" \
            "started yet, waiting..."
        sleep 1
    done

    # Only return $OCF_SUCCESS if _everything_ succeeded as expected
    return ${OCF_SUCCESS}
}


alua_stop() {
    # Exit immediately if configuration is not valid
    alua_validate_all || exit ${?}

    # Check the current resource state
    alua_monitor
    local rc=${?}
    case "${rc}" in
    "${OCF_SUCCESS}")
        # Currently running; normal, expected behavior
        ocf_log info "Resource is currently running."
        ;;
    "${OCF_RUNNING_MASTER}")
        # Running as a Master; need to demote before stopping
        ocf_log info "Resource is currently running as Master."
        alua_demote || ocf_log warn "Demote failed, trying to" \
            "stop anyway..."
        ;;
    "${OCF_NOT_RUNNING}")
        # Currently not running; nothing to do
        ocf_log info "Resource is already stopped."
        return ${OCF_SUCCESS}
        ;;
    esac

    # Check if SCST is loaded
    check_scst

    # Set the local target group to the offline/unavailable ALUA state
    ocf_log debug "alua_stop() -> Setting target group" \
        "'${OCF_RESKEY_local_tgt_grp}' ALUA state to" \
        "'${OCF_RESKEY_remote_inact_state}'..."
    ocf_run scstadmin -noprompt -set_tgrp_attr \
        ${OCF_RESKEY_local_tgt_grp} -dev_group \
        ${OCF_RESKEY_device_group} -attributes \
        state\=${OCF_RESKEY_remote_inact_state}
    if [ ${?} -ne 0 ]; then
        error_msg="Failed to set the local target group to offline!"
        ocf_exit_reason "${error_msg}"
        exit ${OCF_ERR_GENERIC}
    fi

    if ocf_is_true ${OCF_RESKEY_redirect_mode}; then
        # TODO: Using the dev_disk handler would be better if PR pass-through
        # was supported, however, it is not currently. Revisit in the future.
        #redirect_to_real_dev
        # If we're stopping, set all of the devices to inactive for now
        dev_grp_path="${SCST_SYSFS}/device_groups/${OCF_RESKEY_device_group}"
        for i in $(find ${dev_grp_path}/devices -maxdepth 1 -type l); do
            dev_name="$(basename ${i})"
            ocf_log debug "Setting device '${dev_name}' to inactive..."
            echo 0 > ${i}/active
        done
    fi

    # Zap the state file
    rm -f ${OCF_RESKEY_state}

    # Make sure the resource stopped correctly
    while alua_monitor; do
        ocf_log debug "alua_stop() -> Resource has not" \
            "stopped yet, waiting..."
        sleep 1
    done

    # Only return $OCF_SUCCESS if _everything_ succeeded as expected
    return ${OCF_SUCCESS}
}


alua_monitor() {
    # Exit immediately if configuration is not valid
    alua_validate_all || exit ${?}

    # If SCST isn't loaded, then we'll just say the resource isn't running
    if [ ! -d "${SCST_SYSFS}" ]; then
        ocf_log debug "It appears SCST isn't running/loaded yet..."
        return ${OCF_NOT_RUNNING}
    fi

    # Make sure the ALUA configuration is legit
    check_alua

    # Determine our status using the state file
    local rc
    if check_state "master"; then
        ocf_log debug "alua_monitor() -> Resource is running (Master)."
        crm_master -l reboot -v 100
        rc=${OCF_RUNNING_MASTER}
    elif check_state "slave"; then
        ocf_log debug "alua_monitor() -> Resource is running."
        crm_master -l reboot -v 100
        rc=${OCF_SUCCESS}
    elif [ -f ${OCF_RESKEY_state} ]; then
        ocf_log err "The '${OCF_RESKEY_state}' state file exists, but" \
            "contains an unexpected value: $(cat ${OCF_RESKEY_state})"
        rc=${OCF_ERR_GENERIC}
        return ${rc}
    else
        ocf_log debug "alua_monitor() -> Resource is not running."
        crm_master -l reboot -D
        rc=${OCF_NOT_RUNNING}
        return ${rc}
    fi

    # Don't complete the steps below if this is a probe or non-monitor op
    if ocf_is_probe || [ "x${__OCF_ACTION}" != "xmonitor" ]; then
        ocf_log debug "alua_monitor() -> Probe or non-monitor operation," \
            "returning early..."
        return ${rc}
    fi

    # Set target group paths for use below
    dev_grp_path="${SCST_SYSFS}/device_groups/${OCF_RESKEY_device_group}"
    l_tgt_grp_path="${dev_grp_path}/target_groups/${OCF_RESKEY_local_tgt_grp}"
    l_tgt_grp_state="$(head -1 ${l_tgt_grp_path}/state)"
    r_tgt_grp_path="${dev_grp_path}/target_groups/${OCF_RESKEY_remote_tgt_grp}"
    r_tgt_grp_state="$(head -1 ${r_tgt_grp_path}/state)"
    ocf_log debug "alua_monitor() -> SCST local target" \
        "group state: ${l_tgt_grp_state}"

    # We handle detecting if the remote node is offline/unavailable here
    if remote_inactive; then
        # Remote does not exist, so set the remote target group to offline
        if [ "x${r_tgt_grp_state}" != \
            "x${OCF_RESKEY_remote_inact_state}" ]; then
            ocf_log debug "alua_monitor() -> Setting target group" \
                "'${OCF_RESKEY_remote_tgt_grp}' ALUA state to" \
                "'${OCF_RESKEY_remote_inact_state}'..."
            ocf_run scstadmin -noprompt -set_tgrp_attr \
                ${OCF_RESKEY_remote_tgt_grp} -dev_group \
                ${OCF_RESKEY_device_group} -attributes \
                state\=${OCF_RESKEY_remote_inact_state}
                if [ ${?} -ne 0 ]; then
                    error_msg="Failed to set the remote "
                    error_msg+="target group to offline!"
                    ocf_exit_reason "${error_msg}"
                    exit ${OCF_ERR_GENERIC}
                fi
        fi
    else
        if [ ${rc} -eq ${OCF_RUNNING_MASTER} ]; then
            # Remote exists and we're Master, so set the remote to Slave
            if [ "x${r_tgt_grp_state}" != "x${OCF_RESKEY_s_alua_state}" ]; then
                ocf_log debug "alua_monitor() -> Setting target group" \
                    "'${OCF_RESKEY_remote_tgt_grp}' ALUA state to" \
                    "'${OCF_RESKEY_s_alua_state}'..."
                ocf_run scstadmin -noprompt -set_tgrp_attr \
                    ${OCF_RESKEY_remote_tgt_grp} -dev_group \
                    ${OCF_RESKEY_device_group} -attributes \
                    state\=${OCF_RESKEY_s_alua_state}
                    if [ ${?} -ne 0 ]; then
                        error_msg="Failed to set the remote target group state!"
                        ocf_exit_reason "${error_msg}"
                        exit ${OCF_ERR_GENERIC}
                    fi
            fi
        else
            # Remote exists and we're Slave, so set the remote to Master
            if [ "x${r_tgt_grp_state}" != "x${OCF_RESKEY_m_alua_state}" ]; then
                ocf_log debug "alua_monitor() -> Setting target group" \
                    "'${OCF_RESKEY_remote_tgt_grp}' ALUA state to" \
                    "'${OCF_RESKEY_m_alua_state}'..."
                ocf_run scstadmin -noprompt -set_tgrp_attr \
                    ${OCF_RESKEY_remote_tgt_grp} -dev_group \
                    ${OCF_RESKEY_device_group} -attributes \
                    state\=${OCF_RESKEY_m_alua_state}
                    if [ ${?} -ne 0 ]; then
                        error_msg="Failed to set the remote target group state!"
                        ocf_exit_reason "${error_msg}"
                        exit ${OCF_ERR_GENERIC}
                    fi
            fi
        fi
    fi

    if ocf_is_true ${OCF_RESKEY_redirect_mode}; then
        # Resource starts as Slave, but the redirected device might not be
        # available then, so we check it's set correctly here
        if [ ${rc} -eq ${OCF_SUCCESS} ]; then
            bio_dev_to_redirect
            if [ ${?} -eq 0 ] && \
                [ "x${l_tgt_grp_state}" != "x${OCF_RESKEY_s_alua_state}" ]; then
                # Make sure all are redirected before setting this
                ocf_log debug "alua_monitor() -> Setting target group" \
                    "'${OCF_RESKEY_local_tgt_grp}' ALUA state to" \
                    "'${OCF_RESKEY_s_alua_state}'..."
                ocf_run scstadmin -noprompt -set_tgrp_attr \
                    ${OCF_RESKEY_local_tgt_grp} -dev_group \
                    ${OCF_RESKEY_device_group} -attributes \
                    state\=${OCF_RESKEY_s_alua_state}
                if [ ${?} -ne 0 ]; then
                    error_msg="Failed to set the local target group state!"
                    ocf_exit_reason "${error_msg}"
                    exit ${OCF_ERR_GENERIC}
                fi
            fi
        fi
        # If we're Master and we use the ALUA transition state, make sure we
        # are advertising the correct state (left over from transitioning)
        if [ ${rc} -eq ${OCF_RUNNING_MASTER} ] && \
            ocf_is_true ${OCF_RESKEY_use_trans_state}; then
            if [ "x${l_tgt_grp_state}" != "x${OCF_RESKEY_m_alua_state}" ]; then
                bio_dev_to_real
                if [ ${?} -eq 0 ]; then
                    ocf_log debug "alua_monitor() -> Setting target group" \
                        "'${OCF_RESKEY_local_tgt_grp}' ALUA state to" \
                        "'${OCF_RESKEY_m_alua_state}'..."
                    ocf_run scstadmin -noprompt -set_tgrp_attr \
                        ${OCF_RESKEY_local_tgt_grp} -dev_group \
                        ${OCF_RESKEY_device_group} -attributes \
                        state\=${OCF_RESKEY_m_alua_state}
                    if [ ${?} -ne 0 ]; then
                        error_msg="Failed to set the local target group state!"
                        ocf_exit_reason "${error_msg}"
                        exit ${OCF_ERR_GENERIC}
                    fi
                fi
            fi
        fi
    fi

    return ${rc}
}


alua_validate_all() {
    # Test for required binaries
    check_binary scstadmin

    # Make sure the state directory is writable
    state_dir="$(dirname ${OCF_RESKEY_state})"
    touch "${state_dir}/${$}"
    if [ ${?} != 0 ]; then
        ocf_exit_reason "The state file '${OCF_RESKEY_state}' is not writable!"
        return ${OCF_ERR_ARGS}
    fi
    rm -f "${state_dir}/${$}"

    # There can only be one instance of SCST running per node
    if [ ! -z "${OCF_RESKEY_CRM_meta_clone_node_max}" ] &&
        [ "${OCF_RESKEY_CRM_meta_clone_node_max}" -ne 1 ]; then
        error_msg="The 'clone-node-max' parameter must equal '1'."
        ocf_exit_reason "${error_msg}"
        exit ${OCF_ERR_CONFIGURED}
    fi

    # Check the ALUA parameters (make sure they are set)
    if [ -z "${OCF_RESKEY_device_group}" ]; then
        error_msg="The 'device_group' parameter is not set!"
        ocf_exit_reason "${error_msg}"
        exit ${OCF_ERR_CONFIGURED}
    fi
    if [ -z "${OCF_RESKEY_local_tgt_grp}" ]; then
        error_msg="The 'local_tgt_grp' parameter is not set!"
        ocf_exit_reason "${error_msg}"
        exit ${OCF_ERR_CONFIGURED}
    fi
    if [ -z "${OCF_RESKEY_remote_tgt_grp}" ]; then
        error_msg="The 'remote_tgt_grp' parameter is not set!"
        ocf_exit_reason "${error_msg}"
        exit ${OCF_ERR_CONFIGURED}
    fi
    if [ -z "${OCF_RESKEY_m_alua_state}" ]; then
        error_msg="The 'm_alua_state' parameter is not set!"
        ocf_exit_reason "${error_msg}"
        exit ${OCF_ERR_CONFIGURED}
    fi
    if [ -z "${OCF_RESKEY_s_alua_state}" ]; then
        error_msg="The 's_alua_state' parameter is not set!"
        ocf_exit_reason "${error_msg}"
        exit ${OCF_ERR_CONFIGURED}
    fi
    # Currently, we only support using one Master with this RA
    if [ ! -z "${OCF_RESKEY_CRM_meta_master_max}" ] &&
        [ "${OCF_RESKEY_CRM_meta_master_max}" -ne 1 ]; then
        error_msg="The 'master-max' parameter must equal '1'."
        ocf_exit_reason "${error_msg}"
        exit ${OCF_ERR_CONFIGURED}
    fi
    if [ ! -z "${OCF_RESKEY_CRM_meta_master_node_max}" ] &&
        [ "${OCF_RESKEY_CRM_meta_master_node_max}" -ne 1 ]; then
        error_msg="The 'master-node-max' parameter must equal '1'."
        ocf_exit_reason "${error_msg}"
        exit ${OCF_ERR_CONFIGURED}
    fi

    return ${OCF_SUCCESS}
}


alua_meta_data() {
	cat <<-EOF
	<?xml version="1.0"?>
	<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
	<resource-agent name="alua" version="0.1">
	  <version>0.1</version>
	  <longdesc lang="en">The "ALUA states" SCST OCF resource agent for ESOS; this RA only manages SCST's implicit ALUA states.</longdesc>
	  <shortdesc lang="en">ALUA states OCF RA script for ESOS.</shortdesc>
	  <parameters>
	    <parameter name="device_group" unique="1" required="1">
	      <longdesc lang="en">The name of the SCST device group (unique to cluster configuration).</longdesc>
	      <shortdesc lang="en">The 'device_group' parameter.</shortdesc>
	      <content type="string" default="" />
	    </parameter>
	    <parameter name="local_tgt_grp" unique="1" required="1">
	      <longdesc lang="en">The name of the SCST local target group (unique to cluster configuration).</longdesc>
	      <shortdesc lang="en">The 'local_tgt_grp' parameter.</shortdesc>
	      <content type="string" default="" />
	    </parameter>
	    <parameter name="remote_tgt_grp" unique="1" required="1">
	      <longdesc lang="en">The name of the SCST remote target group (unique to cluster configuration).</longdesc>
	      <shortdesc lang="en">The 'remote_tgt_grp' parameter.</shortdesc>
	      <content type="string" default="" />
	    </parameter>
	    <parameter name="m_alua_state" unique="0" required="1">
	      <longdesc lang="en">The ALUA state (eg, active) for a Master node.</longdesc>
	      <shortdesc lang="en">The 'm_alua_state' parameter.</shortdesc>
	      <content type="string" default="active" />
	    </parameter>
	    <parameter name="s_alua_state" unique="0" required="1">
	      <longdesc lang="en">The ALUA state (eg, nonoptimized) for a Slave node.</longdesc>
	      <shortdesc lang="en">The 's_alua_state' parameter.</shortdesc>
	      <content type="string" default="nonoptimized" />
	    </parameter>
	    <parameter name="use_trans_state" unique="0" required="0">
	      <longdesc lang="en">Use the "transitioning" ALUA state before changing target group states.</longdesc>
	      <shortdesc lang="en">The 'use_trans_state' parameter.</shortdesc>
	      <content type="boolean" default="false" />
	    </parameter>
	    <parameter name="set_dev_active" unique="0" required="0">
	      <longdesc lang="en">Set any vdisk_blockio devices for the given device group to active/inactive (1/0) on promotion/demotion.</longdesc>
	      <shortdesc lang="en">The 'set_dev_active' parameter.</shortdesc>
	      <content type="boolean" default="false" />
	    </parameter>
	    <parameter name="redirect_mode" unique="0" required="0">
	      <longdesc lang="en">On promotion/demotion set all devices belonging to this resource's ALUA device group to use the real/redirected backing device.</longdesc>
	      <shortdesc lang="en">The 'redirect_mode' parameter.</shortdesc>
	      <content type="boolean" default="false" />
	    </parameter>
	    <parameter name="redirect_tgt" unique="0" required="0">
	      <longdesc lang="en">The SCST target name used for internal redirection (used with redirect_mode).</longdesc>
	      <shortdesc lang="en">The 'redirect_tgt' parameter.</shortdesc>
	      <content type="string" default="" />
	    </parameter>
	    <parameter name="redirect_grp" unique="0" required="0">
	      <longdesc lang="en">The SCST security group name that belongs to the redirection target (used with redirect_mode).</longdesc>
	      <shortdesc lang="en">The 'redirect_grp' parameter.</shortdesc>
	      <content type="string" default="" />
	    </parameter>
	    <parameter name="issue_lip" unique="0" required="0">
	      <longdesc lang="en">Issue Fibre Channel LIP requests during key operations (may improve initiator responsiveness during fail-over or fail-back).</longdesc>
	      <shortdesc lang="en">The 'issue_lip' parameter.</shortdesc>
	      <content type="boolean" default="false" />
	    </parameter>
	    <parameter name="remote_inact_state" unique="0" required="0">
	      <longdesc lang="en">The ALUA state for an inactive / offline node.</longdesc>
	      <shortdesc lang="en">The 'remote_inact_state' parameter.</shortdesc>
	      <content type="string" default="offline" />
	    </parameter>
	    <parameter name="state" unique="1" required="0">
	      <longdesc lang="en">Full path to the resource state file.</longdesc>
	      <shortdesc lang="en">The 'state' parameter.</shortdesc>
	      <content type="string" default="${HA_RSCTMP}/alua-{OCF_RESOURCE_INSTANCE}.state" />
	    </parameter>
	  </parameters>
	  <actions>
	    <action name="meta-data" timeout="5" />
	    <action name="start" timeout="90" />
	    <action name="stop" timeout="60" />
	    <action name="monitor" timeout="20" depth="0" interval="10" role="Master" />
	    <action name="monitor" timeout="20" depth="0" interval="20" role="Slave" />
	    <action name="notify" timeout="20" />
	    <action name="promote" timeout="60" />
	    <action name="demote" timeout="60" />
	    <action name="reload" timeout="20" />
	    <action name="validate-all" timeout="20" />
	  </actions>
	</resource-agent>
	EOF
}


alua_usage() {
    echo "usage: ${0} {start|stop|monitor|validate-all|promote|demote|reload|notify|meta-data}"
    echo ""
    echo "Expects to have a fully populated OCF RA-compliant environment set."
}


update_state() {
    echo ${1} > ${OCF_RESKEY_state}
}


check_state() {
    target=${1}
    if [ -f ${OCF_RESKEY_state} ]; then
        state=$(cat ${OCF_RESKEY_state})
        if [ "x${target}" = "x${state}" ]; then
            return ${OCF_SUCCESS}
        fi
    else
        if [ "x${target}" = "x" ]; then
            return ${OCF_SUCCESS}
        fi
    fi
    return ${OCF_ERR_GENERIC}
}


remote_inactive() {
    # Determine if there is an inactive (stopped) instance
    crm_mon --as-xml | \
        grep "resource.*id=\"${OCF_RESOURCE_INSTANCE}\".*active=\"false\"" \
        > /dev/null 2>&1
    return ${?}
}


set_tpg_trans() {
    # Set the local + remote target port groups to the transitioning state
    ocf_log debug "${FUNCNAME[1]}() -> Setting target group" \
        "'${OCF_RESKEY_local_tgt_grp}' ALUA state to" \
        "'${TRANSITION_STATE}'..."
    ocf_run scstadmin -noprompt -set_tgrp_attr \
        ${OCF_RESKEY_local_tgt_grp} -dev_group \
        ${OCF_RESKEY_device_group} -attributes \
        state\=${TRANSITION_STATE}
    if [ ${?} -ne 0 ]; then
        error_msg="Failed to set the local target group state!"
        ocf_exit_reason "${error_msg}"
        exit ${OCF_ERR_GENERIC}
    fi
    ocf_log debug "${FUNCNAME[1]}() -> Setting target group" \
        "'${OCF_RESKEY_remote_tgt_grp}' ALUA state to" \
        "'${TRANSITION_STATE}'..."
    ocf_run scstadmin -noprompt -set_tgrp_attr \
        ${OCF_RESKEY_remote_tgt_grp} -dev_group \
        ${OCF_RESKEY_device_group} -attributes \
        state\=${TRANSITION_STATE}
    if [ ${?} -ne 0 ]; then
        error_msg="Failed to set the remote target group state!"
        ocf_exit_reason "${error_msg}"
        exit ${OCF_ERR_GENERIC}
    fi
    # TODO: It appears this is no longer needed, and when used it seems to
    # cause a hang when two calls are executed in parallel.
    #trigger_suspend_resume
}

block_scst_devs() {
    # Block all devices that are members of our device group
    ocf_log info "Blocking all '${OCF_RESKEY_device_group}' devices..."
    dev_grp_path="${SCST_SYSFS}/device_groups/${OCF_RESKEY_device_group}"
    for i in $(find ${dev_grp_path}/devices -type l -maxdepth 1); do
        echo 1 > ${i}/block &
    done
    ocf_log debug "Waiting for devices to finish blocking..."
    wait
}


unblock_scst_devs() {
    # Unblock all devices that are members of our device group
    ocf_log info "Unblocking all '${OCF_RESKEY_device_group}' devices..."
    dev_grp_path="${SCST_SYSFS}/device_groups/${OCF_RESKEY_device_group}"
    for i in $(find ${dev_grp_path}/devices -type l -maxdepth 1); do
        echo 0 > ${i}/block
    done
}


trigger_suspend_resume() {
    # For just one member device of our device group, write to a benign
    # attribute (eg, size) which causes I/O to suspend and then resume
    ocf_log info "Suspending and resuming I/O globally..."
    dev_grp_path="${SCST_SYSFS}/device_groups/${OCF_RESKEY_device_group}"
    for i in $(find ${dev_grp_path}/devices -type l -maxdepth 1); do
        ocf_log debug "Rewriting size attribute value for device '${i}'..."
        size_val="$(head -1 ${i}/size)"
        ocf_log debug "Size attribute value: ${size_val}"
        echo ${size_val} > ${i}/size
        break
    done
}


redirect_to_real_dev() {
    # Change the LUN backing device from the redirected device to the real one
    ocf_log info "Changing redirected device to real one for LUN's on all" \
        "'${OCF_RESKEY_device_group}' devices..."
    dev_grp_path="${SCST_SYSFS}/device_groups/${OCF_RESKEY_device_group}"
    for i in $(find ${dev_grp_path}/devices -maxdepth 1 -type l); do
        dev_name="$(basename ${i})"
        ocf_log debug "Getting H:C:T:L value for SCST device '${dev_name}'..."
        SAVED_IFS=${IFS}
        IFS=$(echo -en "\n\b")
        for j in $(lsscsi); do
            vendor="$(echo ${j} | awk '{print $3}' | tr -d \"[:space:]\")"
            product="$(echo ${j} | awk '{print $4}' | tr -d \"[:space:]\")"
            # The vendor may vary, so we assume product should be unique
            if [ "x${product}" = "x${dev_name}" ]; then
                scsi_hctl="$(echo ${j} | awk '{print $1}' | \
                    tr -d \"[:space:]\" | tr -d [ | tr -d ])"
            fi
        done
        IFS=${SAVED_IFS}
        ocf_log debug "H:C:T:L value detected as '${scsi_hctl}'..."
        if [ -n "${scsi_hctl}" ]; then
            dev_exports="/sys/kernel/scst_tgt/devices/${scsi_hctl}/exported"
            for j in $(ls ${dev_exports}); do
                lun_path="$(readlink -f ${dev_exports}/${j})"
                if echo ${lun_path} | \
                    egrep 'copy_manager_tgt|redirect' \
                    > /dev/null 2>&1; then
                    continue
                fi
                lun_num="$(basename ${lun_path})"
                ocf_log debug "Replacing LUN '${lun_num}' device" \
                    "with '${dev_name}'..."
                echo "replace_no_ua ${dev_name} ${lun_num}" \
                    > ${lun_path}/../mgmt
            done
            # Now we can close the H:C:T:L dev_disk device
            #ocf_log info "Closing the '${scsi_hctl}' dev_disk device..."
            #ocf_run scstadmin -noprompt -close_dev ${scsi_hctl} \
            #    -handler dev_disk
        else
            ocf_log warn "The H:C:T:L value could not be found for" \
                "device '${dev_name}', skipping..."
            continue
        fi
    done
}


real_to_redirect_dev() {
    # Change the LUN backing device from the real device to the redirected one
    ocf_log info "Changing real device to redirected one for LUN's on all" \
        "'${OCF_RESKEY_device_group}' devices..."
    dev_grp_path="${SCST_SYSFS}/device_groups/${OCF_RESKEY_device_group}"
    for i in $(find ${dev_grp_path}/devices -maxdepth 1 -type l); do
        dev_name="$(basename ${i})"
        ocf_log debug "Getting H:C:T:L value for SCST device '${dev_name}'..."
        SAVED_IFS=${IFS}
        IFS=$(echo -en "\n\b")
        for j in $(lsscsi); do
            vendor="$(echo ${j} | awk '{print $3}' | tr -d \"[:space:]\")"
            product="$(echo ${j} | awk '{print $4}' | tr -d \"[:space:]\")"
            # The vendor may vary, so we assume product should be unique
            if [ "x${product}" = "x${dev_name}" ]; then
                scsi_hctl="$(echo ${j} | awk '{print $1}' | \
                    tr -d \"[:space:]\" | tr -d [ | tr -d ])"
            fi
        done
        IFS=${SAVED_IFS}
        ocf_log debug "H:C:T:L value detected as '${scsi_hctl}'..."
        if [ -n "${scsi_hctl}" ]; then
            # Now we can open the H:C:T:L dev_disk device
            ocf_log info "Opening the '${scsi_hctl}' dev_disk device..."
            ocf_run scstadmin -noprompt -open_dev ${scsi_hctl} \
                -handler dev_disk
            dev_exports="/sys/kernel/scst_tgt/devices/${dev_name}/exported"
            for j in $(ls ${dev_exports}); do
                lun_path="$(readlink -f ${dev_exports}/${j})"
                if echo ${lun_path} | \
                    egrep 'copy_manager_tgt|redirect' \
                    > /dev/null 2>&1; then
                    continue
                fi
                lun_num="$(basename ${lun_path})"
                ocf_log debug "Replacing LUN '${lun_num}' device" \
                    "with '${scsi_hctl}'..."
                echo "replace_no_ua ${scsi_hctl} ${lun_num}" \
                    > ${lun_path}/../mgmt
            done
        else
            ocf_log warn "The H:C:T:L value could not be found for" \
                "device '${dev_name}', skipping..."
            continue
        fi
    done
}


get_real_filename() {
    # Lookup the real backing device name from the map file
    dev_name="${1}"
    if [ ! -f "${SCST_DEV_MAPS}/${dev_name}" ]; then
        ocf_log err "No SCST device map file found for device '${dev_name}'!"
        echo ""
    fi
    filename="$(head -1 ${SCST_DEV_MAPS}/${dev_name})"
    ocf_log debug "Real backing device for '${dev_name}': ${filename}"
    echo ${filename}
}


get_redirect_filename() {
    # Find the redirected (iSCSI) block device from lsscsi output
    dev_name="${1}"
    filename=""
    SAVED_IFS=${IFS}
    IFS=$(echo -en "\n\b")
    for i in $(lsscsi); do
        vendor="$(echo ${i} | awk '{print $3}' | tr -d \"[:space:]\")"
        product="$(echo ${i} | awk '{print $4}' | tr -d \"[:space:]\")"
        # The vendor may vary, so we assume product should be unique
        if [ "x${product}" = "x${dev_name}" ]; then
            filename="$(echo ${i} | awk '{print $6}' | tr -d \"[:space:]\")"
            break
        fi
    done
    IFS=${SAVED_IFS}
    ocf_log debug "Redirected backing device for '${dev_name}': ${filename}"
    echo ${filename}
}


test_if_dev_ready() {
    # Perform several checks on the given block device to see if it's working
    block_dev="${1}"
    ocf_log debug "Executing 'sg_turs' on '${block_dev}'..."
    ocf_run timeout ${SG_CMD_TIMEOUT} sg_turs ${block_dev}
    sg_cmd_rc=${?}
    if [ ${sg_cmd_rc} != 0 ]; then
        ocf_log warn "The backing SCSI disk '${block_dev}'" \
            "is not ready yet (TUR failed), skipping..."
        return 1
    fi
    ocf_log debug "Executing 'sg_read' on '${block_dev}'..."
    ocf_run timeout ${SG_CMD_TIMEOUT} sg_read if=${block_dev} bs=512 count=1
    sg_cmd_rc=${?}
    if [ ${sg_cmd_rc} != 0 ]; then
        ocf_log warn "The backing SCSI disk '${block_dev}'" \
            "is not ready yet (read failed), skipping..."
        return 1
    fi
    return 0
}


bio_dev_to_real() {
    # We assume success unless we encounter a problem below
    local rc=0
    local deact_devs=""
    local chg_filenames=""
    local act_devs=""
    # Loop over each SCST device in the device group and modify the filename
    dev_grp_path="${SCST_SYSFS}/device_groups/${OCF_RESKEY_device_group}"
    for i in $(find ${dev_grp_path}/devices -maxdepth 1 -type l); do
        dev_name="$(basename ${i})"
        if [ -f "${i}/active" ]; then
            # Check if the current filename value is correct
            new_filename="$(get_real_filename ${dev_name})"
            if [ -z "${new_filename}" ]; then
                ocf_log warn "A valid backing block device was not" \
                    "found, skipping..."
                rc=1
                continue
            fi
            filename_val="$(head -1 ${i}/filename)"
            active_val="$(head -1 ${i}/active)"
            if [ "x${new_filename}" = "x${filename_val}" ]; then
                ocf_log debug "The 'filename' attribute value is already" \
                    "what it should be..."
                if [ "x${active_val}" = "x0" ]; then
                    ocf_log debug "Setting device '${dev_name}' to active..."
                    echo 1 > ${i}/active
                fi
                continue
            fi
            # The current filename value is not accurate, so change it
            if [ "x${active_val}" = "x1" ]; then
                deact_devs+=" ${i}"
            fi
            chg_filenames+=" ${i},${new_filename}"
            act_devs+=" ${i}"
        else
            ocf_log warn "SCST device '${dev_name}' doesn't have an" \
                "'active' attribute... is this not a vdisk_blockio device?"
            rc=1
            continue
        fi
    done
    # Deactivate the devices here and background each call (slow)
    for dev_path in ${deact_devs}; do
        dev_name="$(basename ${dev_path})"
        ocf_log debug "Setting device '${dev_name}' to inactive..."
        echo 0 > ${dev_path}/active &
    done
    if [ -n "${deact_devs}" ]; then
        ocf_log debug "bio_dev_to_real() -> Waiting for devices to" \
            "deactivate..."
        wait
    fi
    # Change the device 'filename' attributes that need it
    for csv_item in ${chg_filenames}; do
        dev_path="$(echo ${csv_item} | cut -d, -f1)"
        dev_name="$(basename ${dev_path})"
        new_filename="$(echo ${csv_item} | cut -d, -f2)"
        ocf_log info "Changing 'filename' attribute for device" \
            "'${dev_name}' to the real backing device..."
        echo "${new_filename}" > ${dev_path}/filename
        if [ ${?} -ne 0 ]; then
            ocf_log warn "Changing the device 'filename' attribute" \
                "failed, check kernel logs!"
            rc=1
            continue
        fi
    done
    # Activate the devices here and background each call (slow)
    for dev_path in ${act_devs}; do
        dev_name="$(basename ${dev_path})"
        ocf_log debug "Setting device '${dev_name}' to active..."
        echo 1 > ${dev_path}/active &
    done
    if [ -n "${act_devs}" ]; then
        ocf_log debug "bio_dev_to_real() -> Waiting for devices to" \
            "activate..."
        wait
    fi
    return ${rc}
}


bio_dev_to_redirect() {
    # We assume success unless we encounter a problem below
    local rc=0
    local deact_devs=""
    local chg_filenames=""
    local act_devs=""
    # Loop over each SCST device in the device group and modify the filename
    dev_grp_path="${SCST_SYSFS}/device_groups/${OCF_RESKEY_device_group}"
    for i in $(find ${dev_grp_path}/devices -maxdepth 1 -type l); do
        dev_name="$(basename ${i})"
        if [ -f "${i}/active" ]; then
            # Check if the current filename value is correct
            new_filename="$(get_redirect_filename ${dev_name})"
            if [ -z "${new_filename}" ]; then
                ocf_log warn "A valid backing block device was not" \
                    "found, rescanning iSCSI sessions/hosts..."
                iscsi_sess_rescan
                scsi_bus_rescan
                rc=1
                continue
            fi
            filename_val="$(head -1 ${i}/filename)"
            active_val="$(head -1 ${i}/active)"
            if [ "x${new_filename}" = "x${filename_val}" ]; then
                ocf_log debug "The 'filename' attribute value is already" \
                    "what it should be..."
                if [ "x${active_val}" = "x0" ]; then
                    iscsi_sess_rescan
                    if ! test_if_dev_ready ${new_filename}; then
                        rc=1
                        continue
                    fi
                    ocf_log debug "Setting device '${dev_name}' to active..."
                    echo 1 > ${i}/active
                fi
                continue
            fi
            # The current filename value is not accurate, so change it
            if [ "x${active_val}" = "x1" ]; then
                deact_devs+=" ${i}"
            fi
            if ! test_if_dev_ready ${new_filename}; then
                rc=1
                continue
            fi
            chg_filenames+=" ${i},${new_filename}"
            act_devs+=" ${i}"
        else
            ocf_log warn "SCST device '${dev_name}' doesn't have an" \
                "'active' attribute... is this not a vdisk_blockio device?"
            rc=1
            continue
        fi
    done
    # Deactivate the devices here and background each call (slow)
    for dev_path in ${deact_devs}; do
        dev_name="$(basename ${dev_path})"
        ocf_log debug "Setting device '${dev_name}' to inactive..."
        echo 0 > ${dev_path}/active &
    done
    if [ -n "${deact_devs}" ]; then
        ocf_log debug "bio_dev_to_redirect() -> Waiting for devices to" \
            "deactivate..."
        wait
    fi
    # Change the device 'filename' attributes that need it
    for csv_item in ${chg_filenames}; do
        dev_path="$(echo ${csv_item} | cut -d, -f1)"
        dev_name="$(basename ${dev_path})"
        new_filename="$(echo ${csv_item} | cut -d, -f2)"
        ocf_log info "Changing 'filename' attribute for device" \
            "'${dev_name}' to the redirected backing device..."
        echo "${new_filename}" > ${dev_path}/filename
        if [ ${?} -ne 0 ]; then
            ocf_log warn "Changing the device 'filename' attribute" \
                "failed, check kernel logs!"
            rc=1
            continue
        fi
    done
    # Activate the devices here and background each call (slow)
    for dev_path in ${act_devs}; do
        dev_name="$(basename ${dev_path})"
        ocf_log debug "Setting device '${dev_name}' to active..."
        echo 1 > ${dev_path}/active &
    done
    if [ -n "${act_devs}" ]; then
        ocf_log debug "bio_dev_to_redirect() -> Waiting for devices to" \
            "activate..."
        wait
    fi
    return ${rc}
}


scsi_bus_rescan() {
    # Find all iSCSI host entries and rescan the SCSI bus on any found
    local host_list=""
    for i in $(find /sys/class/iscsi_host -maxdepth 1 -type l); do
        iscsi_host="$(basename ${i})"
        host_num="$(echo ${iscsi_host} | tr -d "host")"
        if [ -z "${host_list}" ]; then
            host_list="${host_num}"
        else
            host_list="${host_list},${host_num}"
        fi
    done
    if [ -n "${host_list}" ]; then
        ocf_log debug "Issuing a SCSI bus rescan on host(s) '${host_list}'..."
        ocf_run timeout ${RESCAN_SCSI_TIMEOUT} rescan-scsi-bus.sh \
            --hosts=${host_list} --remove
    else
        ocf_log warn "No iSCSI host entries were found! Nothing to rescan!"
    fi
}


iscsi_sess_rescan() {
    # Issue an iSCSI session rescan, and login to targets if needed
    ocf_log debug "Rescanning all running iSCSI initiator sessions..."
    ocf_run timeout ${ISCSIADM_TIMEOUT} iscsiadm --mode session --rescan
    if [ ${?} -eq 21 ]; then
        ocf_log warn "No iSCSI sessions were found, attempting to login..."
        ocf_run timeout ${ISCSIADM_TIMEOUT} iscsiadm --mode node \
            --loginall=automatic
    fi
}


alua_promote() {
    # Exit immediately if configuration is not valid
    alua_validate_all || exit ${?}

    # Check if SCST is loaded
    check_scst

    # Test the resource's current state
    alua_monitor
    local rc=${?}
    case "${rc}" in
    "${OCF_SUCCESS}")
        # Running as Slave; normal, expected behavior
        ocf_log debug "alua_promote() -> Resource is" \
            "currently running as Slave."
        ;;
    "${OCF_RUNNING_MASTER}")
        # Already a Master; unexpected, but not a problem
        ocf_log info "Resource is already running as Master."
        return ${OCF_SUCCESS}
        ;;
    "${OCF_NOT_RUNNING}")
        # Currently not running; need to start before promoting
        ocf_log info "Resource is currently not running."
        alua_start
        ;;
    *)
        # Failed resource; let the cluster manager recover
        ocf_log err "Unexpected error, cannot promote."
        exit ${rc}
        ;;
    esac

    # Set the local target group to the "Master" ALUA state
    check_alua

    # TODO: Don't block devices for now... needs to be re-visited.
    #block_scst_devs

    if ocf_is_true ${OCF_RESKEY_redirect_mode}; then
        # TODO: Using the dev_disk handler would be better if PR pass-through
        # was supported, however, it is not currently. Revisit in the future.
        #redirect_to_real_dev
        # Change the backing device to the real one
        while ! bio_dev_to_real; do
            ocf_log debug "alua_promote() -> Waiting for redirection-to-real" \
                "device setup to complete..."
            sleep 1
        done
        # Map the device as a LUN on the internal redirection target
        dev_grp_path="${SCST_SYSFS}/device_groups/${OCF_RESKEY_device_group}"
        for i in $(find ${dev_grp_path}/devices -maxdepth 1 -type l); do
            dev_name="$(basename ${i})"
            lun_exists=0
            ocf_log info "Adding a LUN mapping for device '${dev_name}'" \
                "to the redirection target..."
            tgt_path="${SCST_SYSFS}/targets/iscsi/${OCF_RESKEY_redirect_tgt}"
            lun_path="${tgt_path}/ini_groups/${OCF_RESKEY_redirect_grp}/luns"
            for j in $(find ${lun_path} -maxdepth 1 -type d \
                ! -path ${lun_path}); do
                lun_dev_path="$(readlink -f ${j}/device)"
                lun_dev_name="$(basename ${lun_dev_path})"
                if [ "x${lun_dev_name}" = "x${dev_name}" ]; then
                    ocf_log warn "Mapped LUN for device '${dev_name}'" \
                        "already exists, skipping..."
                    lun_exists=1
                    break
                fi
            done
            if [ ${lun_exists} -eq 1 ]; then
                continue
            fi
            (
                flock --verbose -w ${FLOCK_TIMEOUT} 200 || continue
                lun_val="$(ls ${lun_path} | grep -v mgmt | sort -nr | head -1)"
                if [ -n "${lun_val}" ]; then
                    lun_val="$(expr ${lun_val} + 1)"
                    ocf_log debug "Attempting to use LUN value '${lun_val}'..."
                    ocf_run scstadmin -add_lun ${lun_val} -driver iscsi \
                        -target ${OCF_RESKEY_redirect_tgt} \
                        -group ${OCF_RESKEY_redirect_grp} -device ${dev_name}
                else
                    ocf_log warn "A suitable LUN value was not found," \
                        "not mapping '${dev_name}' to the redirection target..."
                fi
            ) 200>${LUN_MAP_FLOCK}
        done
    elif ocf_is_true ${OCF_RESKEY_set_dev_active}; then
        ocf_log debug "alua_promote() -> Changing the group's devices to active..."
        for i in $(find ${SCST_SYSFS}/device_groups/${OCF_RESKEY_device_group}/devices/ \
            -name active -type f -follow -maxdepth 2); do
            echo 1 > ${i}
        done
    else
        trigger_suspend_resume
    fi

    # Set the local TPG state for Master
    ocf_log debug "alua_promote() -> Setting target group" \
        "'${OCF_RESKEY_local_tgt_grp}' ALUA state to" \
        "'${OCF_RESKEY_m_alua_state}'..."
    ocf_run scstadmin -noprompt -set_tgrp_attr \
        ${OCF_RESKEY_local_tgt_grp} -dev_group \
        ${OCF_RESKEY_device_group} -attributes \
        state\=${OCF_RESKEY_m_alua_state}
    if [ ${?} -ne 0 ]; then
        error_msg="Failed to set the local target group state!"
        ocf_exit_reason "${error_msg}"
        exit ${OCF_ERR_GENERIC}
    fi

    # TODO: Don't unblock devices for now... needs to be re-visited.
    #unblock_scst_devs

    # Since there can only be one Master, set the remote target group
    if remote_inactive; then
        ocf_log debug "alua_promote() -> Setting target group" \
            "'${OCF_RESKEY_remote_tgt_grp}' ALUA state to" \
            "'${OCF_RESKEY_remote_inact_state}'..."
        ocf_run scstadmin -noprompt -set_tgrp_attr \
            ${OCF_RESKEY_remote_tgt_grp} -dev_group \
            ${OCF_RESKEY_device_group} -attributes \
            state\=${OCF_RESKEY_remote_inact_state}
        if [ ${?} -ne 0 ]; then
            error_msg="Failed to set the remote target group to offline!"
            ocf_exit_reason "${error_msg}"
            exit ${OCF_ERR_GENERIC}
        fi
    else
        ocf_log debug "alua_promote() -> Setting target group" \
            "'${OCF_RESKEY_remote_tgt_grp}' ALUA state to" \
            "'${OCF_RESKEY_s_alua_state}'..."
        ocf_run scstadmin -noprompt -set_tgrp_attr \
            ${OCF_RESKEY_remote_tgt_grp} -dev_group \
            ${OCF_RESKEY_device_group} -attributes \
            state\=${OCF_RESKEY_s_alua_state}
        if [ ${?} -ne 0 ]; then
            error_msg="Failed to set the remote target group state!"
            ocf_exit_reason "${error_msg}"
            exit ${OCF_ERR_GENERIC}
        fi
    fi

    # Set the new resource state
    update_state "master"

    # After the resource has been promoted, check whether the promotion worked
    while true; do
        alua_monitor
        if [ ${?} -eq ${OCF_RUNNING_MASTER} ]; then
            ocf_log info "Resource was promoted successfully."
            break
        else
            ocf_log debug "alua_promote() -> Resource still" \
                "awaiting promotion."
            sleep 1
        fi
    done

    if ocf_is_true ${OCF_RESKEY_issue_lip}; then
        # Issue a delayed Fibre Channel LIP
        ocf_log debug "Issuing a LIP for all drivers and targets..."
        ocf_run sleep 5 && scstadmin -issue_lip
    fi

    # Only return $OCF_SUCCESS if _everything_ succeeded as expected
    return ${OCF_SUCCESS}
}


alua_demote() {
    # Exit immediately if configuration is not valid
    alua_validate_all || exit ${?}

    # Check if SCST is loaded
    check_scst

    # Test the resource's current state
    alua_monitor
    local rc=${?}
    case "${rc}" in
    "${OCF_RUNNING_MASTER}")
        # Running as Master; normal, expected behavior
        ocf_log debug "alua_demote() -> Resource is" \
            "currently running as Master."
        ;;
    "${OCF_SUCCESS}")
        # Already running as Slave; nothing to do
        ocf_log debug "alua_demote() -> Resource is" \
            "currently running as Slave."
        return ${OCF_SUCCESS}
        ;;
    "${OCF_NOT_RUNNING}")
        # Not running; getting a demote action in this state is unexpected
        ocf_log err "Resource is currently not running."
        exit ${OCF_ERR_GENERIC}
        ;;
    *)
        # Failed resource; let the cluster manager recover
        ocf_log err "Unexpected error, cannot demote."
        exit ${rc}
        ;;
    esac

    # Set the local target group to the "Slave" ALUA state
    check_alua

    # TODO: Don't block devices for now... needs to be re-visited.
    #block_scst_devs

    local change_tpg_safe=1
    if ocf_is_true ${OCF_RESKEY_redirect_mode}; then
        # TODO: Using the dev_disk handler would be better if PR pass-through
        # was supported, however, it is not currently. Revisit in the future.
        #real_to_redirect_dev
        ocf_log debug "alua_demote() -> Changing the group's" \
            "devices to inactive..."
        dev_grp_path="${SCST_SYSFS}/device_groups/${OCF_RESKEY_device_group}"
        for i in $(find ${dev_grp_path}/devices/ \
            -name active -type f -follow -maxdepth 2); do
            echo 0 > ${i}
        done
        # Change the backing device to the redirected one
        # TODO: This adds a lot of time to this 'demote' operation; devices
        # will get set to the redirected block devices during a 'monitor'.
        #bio_dev_to_redirect || change_tpg_safe=0
        change_tpg_safe=0
        # Unmap the device LUN from the internal redirection target
        dev_grp_path="${SCST_SYSFS}/device_groups/${OCF_RESKEY_device_group}"
        for i in $(find ${dev_grp_path}/devices -maxdepth 1 -type l); do
            dev_name="$(basename ${i})"
            ocf_log info "Removing the LUN mapping for device '${dev_name}'" \
                "from the redirection target..."
            dev_exports="/sys/kernel/scst_tgt/devices/${dev_name}/exported"
            (
                flock --verbose -w ${FLOCK_TIMEOUT} 200 || continue
                lun_val=""
                for j in $(ls ${dev_exports}); do
                    lun_path="$(readlink -f ${dev_exports}/${j})"
                    if echo ${lun_path} | grep "${OCF_RESKEY_redirect_tgt}" | \
                        grep "${OCF_RESKEY_redirect_grp}" > /dev/null 2>&1; then
                        lun_val="$(basename ${lun_path})"
                        break
                    fi
                done
                if [ -n "${lun_val}" ]; then
                    ocf_run scstadmin -noprompt -rem_lun ${lun_val} \
                        -driver iscsi -target ${OCF_RESKEY_redirect_tgt} \
                        -group ${OCF_RESKEY_redirect_grp}
                else
                    ocf_log warn "No LUN mapping found for device"\
                        "'${dev_name}' on the internal redirection target!"
                fi
            ) 200>${LUN_MAP_FLOCK}
        done
    elif ocf_is_true ${OCF_RESKEY_set_dev_active}; then
        ocf_log debug "alua_demote() -> Changing the group's" \
            "devices to inactive..."
        dev_grp_path="${SCST_SYSFS}/device_groups/${OCF_RESKEY_device_group}"
        for i in $(find ${dev_grp_path}/devices/ \
            -name active -type f -follow -maxdepth 2); do
            echo 0 > ${i}
        done
    else
        trigger_suspend_resume
    fi

    if [ ${change_tpg_safe} -eq 1 ]; then
        # Set the local TPG state for Master
        ocf_log debug "alua_demote() -> Setting target group" \
            "'${OCF_RESKEY_local_tgt_grp}' ALUA state to" \
            "'${OCF_RESKEY_s_alua_state}'..."
        ocf_run scstadmin -noprompt -set_tgrp_attr \
            ${OCF_RESKEY_local_tgt_grp} -dev_group \
            ${OCF_RESKEY_device_group} -attributes \
            state\=${OCF_RESKEY_s_alua_state}
        if [ ${?} -ne 0 ]; then
            error_msg="Failed to set the local target group state!"
            ocf_exit_reason "${error_msg}"
            exit ${OCF_ERR_GENERIC}
        fi
    fi

    # TODO: Don't unblock devices for now... needs to be re-visited.
    #unblock_scst_devs

    if ! ocf_is_true ${OCF_RESKEY_redirect_mode}; then
        # If we're a Slave, we assume the remote side is the Master
        ocf_log debug "alua_demote() -> Setting target group" \
            "'${OCF_RESKEY_remote_tgt_grp}' ALUA state to" \
            "'${OCF_RESKEY_m_alua_state}'..."
        ocf_run scstadmin -noprompt -set_tgrp_attr \
            ${OCF_RESKEY_remote_tgt_grp} -dev_group \
            ${OCF_RESKEY_device_group} -attributes \
            state\=${OCF_RESKEY_m_alua_state}
        if [ ${?} -ne 0 ]; then
            error_msg="Failed to set the remote target group state!"
            ocf_exit_reason "${error_msg}"
            exit ${OCF_ERR_GENERIC}
        fi
    fi

    # Set the new resource state
    update_state "slave"

    # After the resource has been demoted, check whether the demotion worked
    while true; do
        alua_monitor
        if [ ${?} -eq ${OCF_RUNNING_MASTER} ]; then
            ocf_log debug "alua_demote() -> Resource still" \
                "awaiting demotion."
            sleep 1
        else
            ocf_log info "Resource was demoted successfully."
            break
        fi
    done

    if ocf_is_true ${OCF_RESKEY_issue_lip}; then
        # Issue a delayed Fibre Channel LIP
        ocf_log debug "Issuing a LIP for all drivers and targets..."
        ocf_run sleep 5 && scstadmin -issue_lip
    fi

    # Only return $OCF_SUCCESS if _everything_ succeeded as expected
    return ${OCF_SUCCESS}
}


alua_notify() {
    # Handle notification events
    ocf_log debug "alua_notify() -> Received a" \
        "'${OCF_RESKEY_CRM_meta_notify_type}' /" \
        "'${OCF_RESKEY_CRM_meta_notify_operation}' notification."
    case ${OCF_RESKEY_CRM_meta_notify_type} in
    pre)
        # Set TPG's to transitioning before a start/stop/promote/demote occurs
        if ocf_is_true ${OCF_RESKEY_use_trans_state}; then
            set_tpg_trans
        fi
        ;;
    esac

    # Always return success
    return ${OCF_SUCCESS}
}


check_scst() {
    # Make sure is SCST is actually loaded and running
    if [ -e "${SCST_SYSFS}/version" ]; then
        ocf_log debug "Detected SCST version: $(cat ${SCST_SYSFS}/version)"
    else
        ocf_log err "SCST is not running! We're done..."
        exit ${OCF_ERR_INSTALLED}
    fi
}


check_alua() {
    # Make sure the directories exist in the SCST sysfs structure
    if [ ! -d "${SCST_SYSFS}/device_groups/${OCF_RESKEY_device_group}" ]; then
        ocf_log err "The '${OCF_RESKEY_device_group}' device group" \
            "does not exist!"
        exit ${OCF_ERR_INSTALLED}
    fi
    target_groups="${SCST_SYSFS}/device_groups/"
    target_groups+="${OCF_RESKEY_device_group}/target_groups"
    if [ ! -d "${target_groups}/${OCF_RESKEY_local_tgt_grp}" ]; then
        ocf_log err "The '${OCF_RESKEY_local_tgt_grp}' target group" \
            "does not exist!"
        exit ${OCF_ERR_INSTALLED}
    fi
    if [ ! -d "${target_groups}/${OCF_RESKEY_remote_tgt_grp}" ]; then
        ocf_log err "The '${OCF_RESKEY_remote_tgt_grp}' target group" \
            "does not exist!"
        exit ${OCF_ERR_INSTALLED}
    fi

    # Check that the given ALUA states are valid
    local valid_m_alua_state=0
    local valid_s_alua_state=0
    for i in ${ALUA_STATES}; do
        if [ "x${OCF_RESKEY_m_alua_state}" = "x${i}" ]; then
            valid_m_alua_state=1
        fi
        if [ "x${OCF_RESKEY_s_alua_state}" = "x${i}" ]; then
            valid_s_alua_state=1
        fi
    done
    if [ ${valid_m_alua_state} -eq 0 ]; then
        ocf_log err "The 'm_alua_state' value is" \
            "not valid: ${OCF_RESKEY_m_alua_state}"
        exit ${OCF_ERR_INSTALLED}
    fi
    if [ ${valid_s_alua_state} -eq 0 ]; then
        ocf_log err "The 's_alua_state' value is" \
            "not valid: ${OCF_RESKEY_s_alua_state}"
        exit ${OCF_ERR_INSTALLED}
    fi

    # If using redirection mode, check required parameters
    if ocf_is_true ${OCF_RESKEY_redirect_mode}; then
        if [ "x${OCF_RESKEY_redirect_tgt}" = "x" ]; then
            ocf_log err "The 'redirect_tgt' value is not set!"
            exit ${OCF_ERR_INSTALLED}
        fi
        if [ "x${OCF_RESKEY_redirect_grp}" = "x" ]; then
            ocf_log err "The 'redirect_grp' value is not set!"
            exit ${OCF_ERR_INSTALLED}
        fi
        redirect_tgt="${SCST_SYSFS}/targets/iscsi/${OCF_RESKEY_redirect_tgt}"
        if [ ! -d "${redirect_tgt}" ]; then
            ocf_log err "The '${OCF_RESKEY_redirect_tgt}' target" \
                "does not exist!"
            exit ${OCF_ERR_INSTALLED}
        fi
        if [ ! -d "${redirect_tgt}/ini_groups/${OCF_RESKEY_redirect_grp}" ]; then
            ocf_log err "The '${OCF_RESKEY_redirect_grp}' security group" \
                "does not exist for target '${OCF_RESKEY_redirect_tgt}'!"
            exit ${OCF_ERR_INSTALLED}
        fi
    fi
}


# This must always be set
: "${OCF_RESKEY_state=${HA_RSCTMP}/alua-${OCF_RESOURCE_INSTANCE}.state}"


# Make sure meta-data and usage always succeed
case ${__OCF_ACTION} in
meta-data)
    alua_meta_data
    exit ${OCF_SUCCESS}
    ;;
usage|help)
    alua_usage
    exit ${OCF_SUCCESS}
    ;;
esac

# Anything other than meta-data and usage must pass validation
alua_validate_all || exit ${?}

# Translate each action into the appropriate function call
case ${__OCF_ACTION} in
start)
    alua_start
    ;;
stop)
    alua_stop
    ;;
status|monitor)
    alua_monitor
    ;;
notify)
    alua_notify
    ;;
promote)
    alua_promote
    ;;
demote)
    alua_demote
    ;;
reload)
    ocf_log info "Reloading..."
    alua_start
    ;;
validate-all)
    ;;
migrate_to|migrate_from)
    alua_usage
    exit ${OCF_ERR_UNIMPLEMENTED}
    ;;
*)
    alua_usage
    exit ${OCF_ERR_UNIMPLEMENTED}
    ;;
esac

# Log a debug message and exit
rc=${?}
ocf_log debug "${OCF_RESOURCE_INSTANCE} ${__OCF_ACTION} returned: ${rc}"
exit ${rc}

